﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using com.netkow.KinecTunes.Kinect;
using com.netkow.KinecTunes.iTunes;
using System.Threading;

namespace com.netkow.KinecTunes.WinForm
{
    public partial class KinecTunesMain : Form
    {
        private Kinector kinector;
        private BackgroundWorker worker;
        delegate void SetTextCallback(KinecTrack track);
        delegate void SetLogMessageCallback(string textToAdd);

        public KinecTunesMain()
        {
            InitializeComponent();

            this.kinector = new Kinector();
        }

        private void InitializeKinect()
        {
            kinector = new Kinector();
            kinector.TrackPlayedEvent += new Kinector.TrackPlayedEventHandler(OnNewTrackPlayed);
            kinector.CommandNotRecognizedEvent += new Kinector.CommandNotRecognizedEventHandler(OnCommandNotRecognizedEvent);
            kinector.TrackPausedEvent += new Kinector.TrackPausedEventHandler(OnTrackPausedEvent);
            kinector.TrackResumedEvent += new Kinector.TrackResumedEventHandler(OnTrackResumedEvent);
            kinector.TrackStoppedEvent += new Kinector.TrackStoppedEventHandler(OnTrackStoppedEvent);

            KinectLoadInit();

            // load background worker
            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            KinecTrackReturned trackReturned = new KinecTrackReturned
            {
                track = null,
                returnVal = KinecTrackReturned.KinectReturnVals.Loaded
            };

            worker.RunWorkerAsync(trackReturned);
        }

        #region Background Worker thread

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            KinecTrackReturned trackReturned = (KinecTrackReturned)e.Argument;
            KinecTrackReturned.KinectReturnVals valueFound = trackReturned.returnVal;

            switch (valueFound)
            {
                case KinecTrackReturned.KinectReturnVals.Loaded:
                    try
                    {
                        kinector.InitializeKinect();
                    }
                    catch (InvalidOperationException)
                    {
                        MessageBox.Show("There was a problem loading Kinect or iTunes. \n\nPlease make sure all drivers are installed & the Kinect device is plugged in, \nthen please try again.", "ERROR!", MessageBoxButtons.OK);
                        this.lblKinectStatus.ForeColor = Color.Red;
                        this.lblKinectStatus.Text = "Disconnected";
                        trackReturned.returnVal = KinecTrackReturned.KinectReturnVals.Disconnecting;
                    }
                    break;
                case KinecTrackReturned.KinectReturnVals.Disconnecting:
                    kinector.Terminate();
                    break;
            }

            e.Result = trackReturned;
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            KinecTrackReturned trackReturned = (KinecTrackReturned)e.Result;
            KinecTrackReturned.KinectReturnVals valueFound = trackReturned.returnVal;

            switch (valueFound)
            {
                case KinecTrackReturned.KinectReturnVals.Loaded:
                    KinectLoadComplete();
                    break;
                case KinecTrackReturned.KinectReturnVals.TrackPlayed:
                    NewTrackPlayed(trackReturned.track);
                    break;
                case KinecTrackReturned.KinectReturnVals.Disconnecting:
                    this.lblKinectStatus.ForeColor = Color.Red;
                    this.lblKinectStatus.Text = "Disconnected";
                    this.logMessages.Text += " Unable to launch iTunes and/or Kinect.";
                    break;
            }
        }

        #endregion

        /// <summary>
        /// Add a new message to the message Log. Uses Invoke method to make thread-safe UI updates.
        /// </summary>
        /// <param name="text">the text to add to the log message box</param>
        private void SetLogMessageBox(string text)
        {
            if (this.logMessages.InvokeRequired)
            {
                SetLogMessageCallback callback = new SetLogMessageCallback(SetLogMessageBox);
                this.Invoke(callback, new object[] { text });
            }
            else
            {
                logMessages.Text += text;
            }
        }

        private void KinectLoadInit()
        {
            this.lblKinectStatus.ForeColor = Color.Red;
            this.lblKinectStatus.Text = "Disconnected";
            SetLogMessageBox("Loading iTunes and Kinect...please wait.");
        }

        private void KinectLoadComplete()
        {
            this.lblKinectStatus.ForeColor = Color.Green;
            this.lblKinectStatus.Text = "Ready for Commands!";
            this.logMessages.Text += "Ready for Commands!";
        }

        private void NewTrackPlayed(KinecTrack track)
        {
            if (this.lblArtist.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(NewTrackPlayed);
                this.Invoke(d, new object[] { track });
            }
            else
            {
                // Set all Track fields
                this.lblArtist.Text = track.Artist;
                this.lblSong.Text = track.Name;
                this.lblAlbum.Text = track.Album;

                this.logMessages.Text += string.Format(" Now Playing Artist: {0}, Song: {1}\n", track.Artist, track.Name);
            }
        }

        #region Kinect captured events

        private void OnNewTrackPlayed(KinecTrack trackFound)
        {
            KinecTrackReturned trackReturned = new KinecTrackReturned
            {
                track = trackFound,
                returnVal = KinecTrackReturned.KinectReturnVals.TrackPlayed
            };

            worker.RunWorkerAsync(trackReturned);
        }
        
        private void OnTrackPausedEvent(object sender)
        {
            SetLogMessageBox(" iTunes paused song.");
        }

        private void OnTrackResumedEvent(object sender)
        {
            SetLogMessageBox(" iTunes resumed song playback.");
        }

        private void OnTrackStoppedEvent(object sender)
        {
            SetLogMessageBox(" iTunes stopped song.");
        }

        private void OnCommandNotRecognizedEvent(object sender)
        {
            SetLogMessageBox(" Command not recognized. Try again!\n");
        }

        #endregion

        #region FileMenuItems

        private void startToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (kinector.kinectInUse())
            {
                // Reset the Kinect device so only one instance runs at a time.
                this.lblKinectStatus.ForeColor = Color.Red;
                this.lblKinectStatus.Text = "Disconnecting Kinect device...";

                KinecTrackReturned trackReturned = new KinecTrackReturned
                {
                    track = null,
                    returnVal = KinecTrackReturned.KinectReturnVals.Disconnecting
                };

                worker.RunWorkerAsync(trackReturned);
            }

            InitializeKinect();
        }

        private void commandsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MessageBox.Show("KinecTunes - Control iTunes with your voice!\n\nCOMMANDS\nFor each, say \"Kinect\" then:\n\n\"Play\", \"Pause\", \"Stop\"\n\"Play Artist [artist name]\"\n\"Play Song [song name]\"\n\n\nOriginally created by dotNetkow, 2011. Now open to all!",
                "About - Commands", MessageBoxButtons.OK);
        }

        private void onlineFAQToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://kinectunes.codeplex.com/documentation");
        }

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        #endregion
    }

    public class KinecTrackReturned
    {
        public KinecTrack track;
        public KinectReturnVals returnVal;

        public enum KinectReturnVals
        {
            Loaded,
            TrackPlayed,
            Disconnecting
        }
    }
}
